#include "Subsystem/RoomManager/RoomManagerSubsystem.h"

DEFINE_LOG_CATEGORY(RoomManager);

void URoomManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);
}

void URoomManagerSubsystem::Deinitialize()
{
	UE_LOG(RoomManager, Log, TEXT("Remain RoomInfo size [%d], Remain ActorMap size [%d]"), RoomInfoMap.Num(), ActorMap.Num());

	RoomInfoMap.Empty();
	ActorMap.Empty();
	Super::Deinitialize();
}

void URoomManagerSubsystem::AddActorToRoom(const FName& RoomName, AActor* InRoomActor, AActor* InActor)
{
	UE_LOG(RoomManager, Log, TEXT("Add Actor [%s] to RoomActor [%s], RoomName [%s]"), *InActor->GetName(), *InRoomActor->GetName(), *RoomName.ToString());
	if (!IsRoomExist(RoomName))
	{
		UE_LOG(RoomManager, Log, TEXT("Room [%s] not exist, Create new"), *RoomName.ToString());
	}
	FRoomInfo& Info = RoomInfoMap.FindOrAdd(RoomName);

	if (!Info.ActorMap.Contains(InActor))
	{
		AddToRoomImpl(RoomName, InActor);
	}

	FRoomActorInfo& ActorInfo = Info.ActorMap.FindOrAdd(InActor);
	ensureAlwaysMsgf(!ActorInfo.RoomActor.Contains(InRoomActor), TEXT("Actor [%s] already in Room [%s], RoomActor [%s]"), *InActor->GetName(), *RoomName.ToString(), *InRoomActor->GetName());
	ActorInfo.RoomActor.Add(InRoomActor);
}

void URoomManagerSubsystem::RemoveActorFromRoom(const FName& RoomName, AActor* InRoomActor, AActor* InActor)
{
	UE_LOG(RoomManager, Log, TEXT("Remove Actor [%s] from RoomActor [%s], RoomName [%s]"), *InActor->GetName(), *InRoomActor->GetName(), *RoomName.ToString());
	if (!IsRoomExist(RoomName))
	{
		UE_LOG(RoomManager, Warning, TEXT("Room [%s] not exist when remove actor [%s] from room [%s]"), *RoomName.ToString(), *InActor->GetName(), *InRoomActor->GetName());
		return;
	}

	FRoomInfo& Info = RoomInfoMap.FindChecked(RoomName);
	if (!Info.ActorMap.Contains(InActor))
	{
		UE_LOG(RoomManager, Warning, TEXT("Actor [%s] not in Room [%s] when remove from room [%s]"), *InActor->GetName(), *RoomName.ToString(), *InRoomActor->GetName());
		return;
	}

	FRoomActorInfo& ActorInfo = Info.ActorMap.FindChecked(InActor);
	ensureAlwaysMsgf(ActorInfo.RoomActor.Contains(InRoomActor), TEXT("Actor [%s] not in Room [%s], RoomActor [%s]"), *InActor->GetName(), *RoomName.ToString(), *InRoomActor->GetName());
	ActorInfo.RoomActor.Remove(InRoomActor);
	// 空了就真的把 Actor 移除掉
	if (ActorInfo.RoomActor.IsEmpty())
	{
		Info.ActorMap.Remove(InActor);
		RemoveFromRoomImpl(RoomName, InActor);
	}
}

bool URoomManagerSubsystem::IsRoomExist(const FName& RoomName) const
{
	return RoomInfoMap.Contains(RoomName);
}

bool URoomManagerSubsystem::IsActorInRoom(const FName& RoomName, AActor* InActor) const
{
	const FName* RoomNamePtr = ActorMap.Find(InActor);
	return RoomNamePtr && *RoomNamePtr == RoomName;
}

bool URoomManagerSubsystem::IsActorInAnyRoom(AActor* InActor) const
{
	return ActorMap.Contains(InActor);
}

FName URoomManagerSubsystem::GetActorRoomName(AActor* InActor) const
{
	const FName* RoomName = ActorMap.Find(InActor);
	return RoomName ? *RoomName : NAME_None;
}

void URoomManagerSubsystem::AddToRoomImpl(const FName& RoomName, AActor* InActor)
{
	UE_LOG(RoomManager, Log, TEXT("Dispatch [%s] [Enter] room [%s]"), *InActor->GetName(), *RoomName.ToString());
	ActorMap.Add(InActor, RoomName);
	OnActorEnterRoom.Broadcast(RoomName, InActor);
}

void URoomManagerSubsystem::RemoveFromRoomImpl(const FName& RoomName, AActor* InActor)
{
	UE_LOG(RoomManager, Log, TEXT("Dispatch [%s] [Leave] room [%s]"), *InActor->GetName(), *RoomName.ToString());
	OnActorLeaveRoom.Broadcast(RoomName, InActor);
	ActorMap.Remove(InActor);
}
